<?php
require_once 'common.php';
require_once 'OaAppServerModel.php';
require_once 'OaDeveloperModel.php';
require_once 'OaCompanyModel.php';
require_once 'OaCodeModel.php';
require_once 'OaAppConfigOrderModel.php';
require_once 'OaAppServerConfigLogModel.php';
require_once 'OaAuthorityModel.php';
require_once OPEN_LIB_PATH . '/open/business-logic/OaSyncInfoLogic.php';
require_once OA_LIB_PATH . '/inner/service/OAApi.php';
require_once OA_LIB_PATH . '/inner/service/OASApi.php';
require_once OA_LIB_PATH . '/inner/service/SDOServiceDeposit.php';
require_once OA_LIB_PATH . '/inner/service/SDOServicePayment.php';
require_once OA_LIB_PATH . '/inner/service/SDOServiceAuthen.php';
require_once OA_LIB_PATH . '/inner/service/SDOServiceBilling.php';
require_once OA_LIB_PATH . '/inner/service/SDOServiceAddDeposit.php';

/**
 * 应用模型
 * @author wangjiajun
 *
 */
class OaAppModel extends OpModel
{
    // 应用类型
    const APP_TYPE_CLIENT = 1;        // 客户端
    const APP_TYPE_WEB_PAGE = 2;      // 网页
    const APP_TYPE_WEB_SITE = 3;      // 网站
    
    // 应用状态
    const APP_STATUS_DELETE = 0;            // 删除状态
    const APP_STATUS_DEVELOPING = 1;        // 开发
    const APP_STATUS_TESTING = 2;           // 测评
    const APP_STATUS_CONFIGING = 3;         // 正式环境配置
    const APP_STATUS_APPLY_RELEASE = 4;     // 申请上线
    const APP_STATUS_AUDITING = 5;          // 审核
    const APP_STATUS_IN_OPERATION = 6;      // 运营
    
    // 服务类型
    const APP_SERVICE_TYPE_LOGIN = 'login';							// 认证服务
    const APP_SERVICE_TYPE_IBW = 'ibw';								// 圈圈（IBW）服务
    const APP_SERVICE_TYPE_IGW = 'igw';								// 圈圈（IGW）服务
    const APP_SERVICE_TYPE_PAY = 'pay';								// 支付服务
    const APP_SERVICE_TYPE_BILLING = 'billing';						// 计费服务
    const APP_SERVICE_TYPE_HELP = 'help';							// 彩虹服务
    const APP_SERVICE_TYPE_BONUS = 'bonus';							// 分红服务
    const APP_SERVICE_TYPE_POINTS = 'points';						// 积分服务
    const APP_SERVICE_TYPE_PREVENT_OBSESSION = 'prevent_obsession';	// 防沉迷服务
    
    // 服务状态
    const APP_SERVICE_STATUS_UNSELECTED = 0;           // 未选择
    const APP_SERVICE_STATUS_SELECTED = 1;             // 已选择
    const APP_SERVICE_STATUS_OPENED = 2;               // 开通成功
    const APP_SERVICE_STATUS_UNOPENED = 3;             // 开通失败
    const APP_SERVICE_STATUS_CLOSED = 4;     		   // 已关闭
    
    // 消费模式
    const APP_PAY_TYPE_TIME = 1;            // 时长消费
    const APP_PAY_TYPE_PT_POINT = 2;        // pt点消费
    const APP_PAY_TYPE_FREE = 3;            // 免费
    const APP_PAY_TYPE_OA_TRANSFER = 4;     // 点券划账
    const APP_PAY_TYPE_OA_ITEMS = 5;        // 道具计费
    
    // 接入流程
    const APP_JOIN_FLOW_CUSTOMIZE = 1;        // 定制
    const APP_JOIN_FLOW_FAST_SINGLE = 2;      // 快速（单）
    const APP_JOIN_FLOW_FAST_DOUBLE = 3;      // 快速（双）
    
    // 开发环境
    const APP_CONFIG_ENV_TEST = 1;            // 连调
    const APP_CONFIG_ENV_PRODUCTION = 2;      // 运营
    
    // 应用加密方式
    const APP_ENCRYPT_TYPE_MD5 = 1;            // MD5
    const APP_ENCRYPT_TYPE_RSA = 2;            // RSA
    
    //测试账号数量限制
    const APP_TEST_ACCOUNT_LIMIT = 50;
    const SNDA_TEST_ACCOUNT_LIMIT = 50;
    
    //测试账号充值点券数量
    const APP_TEST_ACCOUNT_AMOUNT = 10000000;
    
    //pt账号后缀
    const PT_SUFFIX = ".sdo";
    
    // 应用成员邀请状态
    const APP_INVITE_STATUS_NEW = 0;        // 新邀请
    const APP_INVITE_STATUS_ACCEPTED = 1;   // 已接受
    const APP_INVITE_STATUS_INVALID = 2;    // 已失效
    
    // 应用登录服务器是否需要添加到认证信任列表
    const APP_NOT_NEED_TRUST_IP = 0;        // 不需要
    const APP_NEED_TRUST_IP = 1;            // 需要
    
    //是否盛大游戏
    const IS_SDG_GAME = 1;//是的
    CONST NOT_SDG_GAME = 2;//不是
    const SDG_GAME_SP_ID = 7;//盛大游戏母公司ID
    
    private static $_configColumns = array(
        'service_login_status',
        'service_login',
        'service_ibw_status',
        'service_ibw',
        'service_igw_status',
        'service_igw',
        'service_pay_status',
        'service_pay',
        'service_billing_status',
        'service_billing',
        'service_help_status',
        'service_help',
        'service_bonus_status',
        'service_bonus',
        'service_points_status',
        'service_points',
        'service_prevent_obsession_status',
        'service_prevent_obsession',
        'app_pub_key',
        'snda_pub_key',
        'snda_private_key'
    );
    
    private static $_jsonColumns = array(
        'service_login',
        'service_ibw',
        'service_igw',
        'service_pay',
        'service_billing',
        'service_help',
        'service_bonus',
        'service_points',
        'service_prevent_obsession',
        'ext_info_for_qq'
    );
    
    private $_configEnv;
    private $_oaWebDb;
    private $_appTable;
    private $_appConfigTable;
    private $_appDeveloperTable;
    private $_appAreaTable;
    private $_appGroupTable;
    private $_appServerTable;
    private $_appLoginServerTable;
    private $_appLoginServerValidTable;
    private $_appPayServerTable;
    private $_appPayServerValidTable;
    private $_inviteSndaUserTable;
    private $_appTestAccountTable;
    private $_appDomainTable;
    private $_companyModel;
    private $_appSvrCfgLogModel;
    
    public static $appType = array(
        self::APP_TYPE_CLIENT => array(
            'name' => '客户端游戏'
        ),
        self::APP_TYPE_WEB_PAGE => array(
            'name' => '网页游戏'
        ),
        self::APP_TYPE_WEB_SITE => array(
            'name' => '网站'
        )
    );
    
    public static function getConstantValueByName($name, $default = 0)
    {
        return self::_getConstByName($name, '', $default, 'OaAppModel');
    }
    
    public function __construct($env = self::APP_CONFIG_ENV_PRODUCTION, $logger = null)
    {
        if (is_null($logger)) {
            $logger = getOaModelLogger();
        }
        parent::__construct($logger);
        
        $this->setCurrentConfigEnv($env);
        
        $dbConfig = Oa_Util_Config::getSingleDbConfig(OA_WEB_DB_CLUSTER_ID);
        $db = OpDbModel::getInstance($dbConfig, $logger);
        $this->_oaWebDb = $db;
        $this->_appTable = new OpTableModel($db, 'app', $logger);
        $this->_appConfigTable = new OpTableModel($db, 'app_config', $logger);
        $this->_appDeveloperTable = new OpTableModel($db, 'app_developer', $logger);
        $this->_appAreaTable = new OpTableModel($db, 'app_area', $logger);
        $this->_appGroupTable = new OpTableModel($db, 'app_group', $logger);
        $this->_appServerTable = new OpTableModel($db, 'app_server', $logger);
        $this->_appLoginServerTable = new OpTableModel($db, 'app_login_server', $logger);
        $this->_appLoginServerValidTable = new OpTableModel($db, 'app_login_server_valid', $logger);
        $this->_appPayServerTable = new OpTableModel($db, 'app_pay_server', $logger);
        $this->_appPayServerValidTable = new OpTableModel($db, 'app_pay_server_valid', $logger);
        $this->_inviteSndaUserTable = new OpTableModel($db, 'invite_snda_user', $logger);
        $this->_appTestAccountTable = new OpTableModel($db, 'app_test_account', $logger);
        $this->_appDomainTable = new OpTableModel($db, 'app_domain', $logger);
        $this->_companyModel = new OaCompanyModel($logger);
        $this->_appSvrCfgLogModel = new OaAppServerConfigLogModel($logger);
    }
    
    /**
     * 创建应用
     * @param string $name
     * @param string $pt
     * @param int $companyId
     * @param array $row
     * @return int|boolean 如果应用名已存在，设置错误号1，如果pt帐号不存在，设置错误号2
     */
    public function createApp($name, $pt, $companyId, $row = array())
    {
        $result = User::convertId($pt);
        if ($result[0] != 0) {
            $this->_setError(2, "convert id failed: {$result[1]}");
            return false;
        }
        $creator = $result[2];
        $codeModel = new OaCodeModel($this->_logger);
        if (is_array($this->getAppByName($name)) || 
        	is_array($codeModel->getAppByName($name, self::APP_CONFIG_ENV_PRODUCTION))) {
            $this->_setError(1, "app name already exists");
            return false;
        }
        $row = array_merge(array(
            'name' => $name,
            'creator' => $creator,
            'company_id' => $companyId,
            'encrypt_type' => 1,
            'join_type' => self::APP_JOIN_FLOW_FAST_SINGLE
        ), $row);
        if (!isset($row['status'])) {
            $row['status'] = self::APP_STATUS_DEVELOPING;
        }
        if (!isset($row['create_time'])) {
            $row['create_time'] = date('Y-m-d H:i:s');
        }
        $row = self::_encodeRow($row);
        list($appRow, $appConfigRow) = self::_splitRow($row);
        
        if (isset($appRow['id'])) {
            $appId = (int) $appRow['id'];
            if ($appId <= 0 || $appId >= 100000000) {
                $this->_logger->err("app id invalid: id-$appId");
                return false;
            }
            if (is_array($this->getApp($appId))) {
                $this->_logger->err("app id duplicated: id-$appId");
                return false;
            }
        } else {
            $appId = $this->_generateAppId($companyId == 7);
            if ($appId === false) {
                $this->_logger->err("generate app id failed");
                return false;
            }
        }
        $appRow['id'] = $appId;
        if ($appRow['company_id']==self::SDG_GAME_SP_ID) {
        	$appRow['is_sdg'] = self::IS_SDG_GAME;
        } else {
        	$appRow['is_sdg'] = self::NOT_SDG_GAME;
        }
        
        if (!$this->_appTable->insert($appRow, null ,false)) {
            $this->_logger->err("insert failed");
            return false;
        }
        $appConfigRow['app_id'] = $appId;
        $appConfigRow['environment'] = self::APP_CONFIG_ENV_TEST;
        if (!$this->_appConfigTable->insert($appConfigRow, null, false)) {
            $this->_appTable->delete(array(
                'id' => $appId
            ));
            $this->_logger->err("insert test config failed");
            return false;
        }
        $appConfigRow['environment'] = self::APP_CONFIG_ENV_PRODUCTION;
        if (!$this->_appConfigTable->insert($appConfigRow, null, false)) {
            $this->_appTable->delete(array(
                'id' => $appId
            ));
            $this->_appConfigTable->delete(array(
                'app_id' => $appId
            ));
            $this->_logger->err("insert production config failed");
            return false;
        }
        
        // 设置创建者为应用管理员
        if ($this->addDeveloper($appId, $pt, OaAuthorityModel::ROLE_APP_ADMIN) === false) {
            $this->_appTable->delete(array(
                'id' => $appId
            ));
            $this->_appConfigTable->delete(array(
                'app_id' => $appId
            ));
            $this->_logger->err("add developer failed");
            return false;
        }
        
        // 创建默认区组
        $appServerModel = new OaAppServerModel(self::APP_CONFIG_ENV_TEST, $this->_logger);
        $areaId = $appServerModel->addArea($appId, '0区', 0, array(
            'status' => OaAppServerModel::AREA_STATUS_VALID,
            'area_code' => 0,
        	'environment' => self::APP_CONFIG_ENV_TEST,
            'create_time' => date('Y-m-d H:i:s')
        ));
        if ($areaId === false) {
            $this->_logger->warning("add default area in test failed");
        } else {
            if ($appServerModel->addGroup($areaId, '0组', 0, array(
                'status' => OaAppServerModel::GROUP_STATUS_VALID,
                'create_time' => date('Y-m-d H:i:s'),
                'group_code' => 0
            )) === false) {
                $this->_logger->warning("add default group in test failed");
            }
        }
        $areaId = $appServerModel->addArea($appId, '0区', 0, array(
            'status' => OaAppServerModel::AREA_STATUS_VALID,
            'area_code' => 0,
        	'environment' => self::APP_CONFIG_ENV_PRODUCTION,
            'create_time' => date('Y-m-d H:i:s')
        ));
        if ($areaId === false) {
            $this->_logger->warning("add default area in production failed");
        } else {
            if ($appServerModel->addGroup($areaId, '0组', 0, array(
                'status' => OaAppServerModel::GROUP_STATUS_VALID,
                'create_time' => date('Y-m-d H:i:s'),
                'group_code' => 0
            )) === false) {
                $this->_logger->warning("add default group in production failed");
            }
        }
        return $appId;
    }
    
    /**
     * 查询应用信息
     * @param int $appId
     * @param array $columns
     * @return array|null|boolean
     */
    public function getApp($appId, $columns = null)
    {
        if (is_array($columns)) {
            list($appColumns, $appConfigColumns) = self::_splitColumns($columns);
        } else {
            $appColumns = null;
            $appConfigColumns = null;
        }
        $appRow = array();
        $appConfigRow = array();
        if ((is_array($appColumns) && !empty($appColumns)) || is_null($appColumns)) {
            $appRow = $this->_appTable->getRow(array('id' => $appId), null, $appColumns);
            if ($appRow === false) {
                return false;
            }
            if (is_null($appRow)) {
                return null;
            }
        }
        $appRow = $this->_decodeRow($appRow);
        if ((is_array($appConfigColumns) && !empty($appConfigColumns)) || is_null($appConfigColumns)) {
            $appConfigRow = $this->getAppConfig($appId, $appConfigColumns);
            if ($appConfigRow === false) {
                return false;
            }
            if (is_null($appConfigRow)) {
                return null;
            }
        }
        return array_merge($appRow, $appConfigRow);
    }
    
    /**
     * 根据应用名称查询应用
     * @param string $name
     * @return array|null|boolean
     */
    public function getAppByName($name)
    {
        $apps = $this->getAppList(array(
            'name' => $name
        ));
        if ($apps === false) {
        	return false;
        }
        if (count($apps) > 0) {
        	return $apps[0];
        } else {
        	return null;
        }
    }
    
    /**
     * 设置应用状态
     * @param int $appId
     * @return int|boolean
     */
    public function getAppStatus($appId)
    {
        $app = $this->getApp($appId, array('status'));
        if (is_array($app)) {
            return $app['status'];
        } else {
            return false;
        }
    }
    
    /**
     * 查询多个应用，返回数组以应用ID为Index
     * @param array $appIds
     * @return array|boolean
     */
    public function getAppMulti($appIds)
    {
        if (empty($appIds)) {
            return array();
        }
        foreach ($appIds as &$appId) {
            $appId = (int) $appId;
        }
        unset($appId);
        $sql = "SELECT * FROM app WHERE id IN (" . implode(',', $appIds) . ")";
        $rows = $this->_appTable->query($sql);
        if ($rows === false) {
        	$this->_logger->log("query failed");
            return false;
        }
        $apps = array();
        foreach ($rows as $row) {
            $apps[$row['id']] = $this->_decodeRow($row);
        }
        return $apps;
    }
    /**
     * 根据指定条件查询应用列表
     * @param array $where（无status字段时， 不包含处于删除状态的应用）
     * @param int $offset
     * @param int $count
     * @param string $sort
     * @param array $otherWhere
     * @return array|boolean
     */
    public function getAppList ($where = null, $offset = 0, $count = null, $sort = null, $otherWhere = null)
    {
        if (! isset($where['status']) && ! isset($otherWhere['status'])) {
            $otherWhere['status'] = array(
                'key' => 'status', 
                'operation' => '>', 
                'value' => self::APP_STATUS_DELETE
            );
        }
        $apps = $this->_appTable->get($where, $offset, $count, $sort, $otherWhere);
        if ($apps === false) {
            $this->_logger->log("get failed");
            return false;
        }
        foreach ($apps as &$app) {
            $app = $this->_decodeRow($app);
        }
        unset($app);
        return $apps;
    }
    /**
     * 根据指定条件查询应用总数
     * @param array $where （无status字段时， 不包含处于删除状态的应用）
     * @param array $otherWhere
     * @return int|boolean
     */
    public function getAppListCount ($where = null, $otherWhere = null)
    {
        if (! isset($where['status']) && ! isset($otherWhere['status'])) {
            $otherWhere['status'] = array(
                'key' => 'status', 
                'operation' => '>', 
                'value' => self::APP_STATUS_DELETE
            );
        }
        return $this->_appTable->getCount($where, $otherWhere);
    }
    
    /**
     * 修改应用信息
     * @param int $appId
     * @param array $row
     * @return boolean 如果名称重复，设置错误号1
     */
    public function modifyApp($appId, $row)
    {
    	$app = $this->getApp($appId);
    	if (!is_array($app)) {
    		$this->_logger("app not exists: app-$appId");
    		return false;
    	}
    	if (isset($row['name']) && $row['name'] == $app['name']) {
    		unset($row['name']);
    	}
    	if (empty($row)) {
    		return true;
    	}
    	if (isset($row['name'])) {
	    	$codeModel = new OaCodeModel($this->_logger);
	        if (is_array($this->getAppByName($row['name'])) ) {
	            $this->_setError(1, "app name already exists");
	            return false;
	        }
	        $codeApp = $codeModel->getAppByName($row['name'], OaAppModel::APP_CONFIG_ENV_PRODUCTION);
	        if($codeApp && $codeApp['SERVICE_ID'] != $appId) {
                $this->_setError(1, "app name already exists");
                return false;
	        }
    	}
        $row = $this->_encodeRow($row);
        list($appRow, $appConfigRow) = self::_splitRow($row);
        if (!empty($appRow)) {
            if ($this->_appTable->update(array('id' => $appId), $appRow) === false) {
                $this->_logger->log("update table {$this->_table} failed");
                return false;
            }
        }
        if (!empty($appConfigRow)) {
            if ($this->_appConfigTable->update(
            array(
                'environment' => $this->_configEnv, 
                'app_id' => $appId
            ), $appConfigRow) === false) {
                $this->_logger->log("update table {$this->_table} failed");
                return false;
            }
        }
        return true;
    }
    
    /**
     * 设置应用状态
     * @param int $appId
     * @param int $status
     * @return boolean
     */
    public function setAppStatus($appId, $status)
    {
        return $this->modifyApp($appId, array(
            'status' => $status
        ));
    }
	/**
     * 设置应用是否是盛大游戏
     * @param int $appId
     * @param int $is
     * @return boolean
     */
    public function setAppIsSDGGame($appId, $isSdg=false)
    {
        if (!$isSdg) {
        	$isSdg = self::NOT_SDG_GAME;
        } else {
        	$isSdg = self::IS_SDG_GAME;
        }
    	return $this->modifyApp($appId, array(
            'is_sdg' => $isSdg
        ));
    }
    /**
     * 删除应用，非物理删除
     * @param int $appId
     * 
     * @return boolean
     */
    public function deleteApp($appId)
    {
        return $this->setAppStatus($appId, self::APP_STATUS_DELETE);
    }
    
    /**
     * 添加应用开发人员
     * @param int $appId
     * @param string $pt
     * @param int $role
     * @return boolean 如果pt帐号不存在，设置错误号1，如果已是应用人员，设置错误号2
     */
    public function addDeveloper($appId, $pt, $role = OaAuthorityModel::ROLE_APP_DEVELOPER) 
    {        
        $result = User::convertId($pt);
        if ($result[0] != 0) {
            $this->_setError(1, "convert id failed: {$result[1]}");
            return false;
        }
        $developerId = $result[2];
        
        // 不允许重复添加
        if (is_array($this->getDeveloper($appId, $developerId))) {
            $this->_setError(2, "already in app");
            return false;
        }
        
        // 如果该开发者还不属于应用所属公司，先加入公司
        $app = $this->getApp($appId, array('company_id'));
        if ($app === false) {
            $this->_logger->log("get app failed");
            return false;
        }
        if (is_null($app)) {
            $this->_logger->log("app not exist");
            return false;
        }
        $companyId = $app['company_id'];
        if (is_null($this->_companyModel->getDeveloper($companyId, $developerId))) {
            if ($this->_companyModel->addDeveloper($companyId, $pt) === false) {
                $this->_logger->log("add developer to company failed");
                return false;
            }
        }
        
        // 如果添加的是应用管理员且该人员在公司的角色为普通员工，升级到产品经理
        if ($role == OaAuthorityModel::ROLE_APP_ADMIN) {
            $developer = $this->_companyModel->getDeveloper($companyId, $developerId);
            if (!is_array($developer)) {
                $this->_logger->log("developer not in company");
                return false;
            }
            if ($developer['role'] == OaAuthorityModel::ROLE_COMPANY_EMPLOYEE && ! $this->_companyModel->changeDeveloperRole(
            $companyId, $developerId, OaAuthorityModel::ROLE_COMPANY_PM)) {
                $this->_logger->log("change developer role in company failed");
                return false;
            }
        }
        return $this->_appDeveloperTable->insert(
        array(
            'app_id' => $appId, 
            'developer_id' => $developerId, 
            'role' => $role
        ), null, false);
    }
    /**
     * 查询应用开发人员
     * @param int $appId
     * @param int $developerId
     * @return array|null|boolean
     */
    public function getDeveloper ($appId, $developerId)
    {
        return $this->_appDeveloperTable->getRow(
        array(
            'app_id' => $appId, 
            'developer_id' => $developerId
        ));
    }
    
    /**
     * 查询应用开发人员列表
     * @param int $appId
     * @param int $role 在产品中的角色，null表示任意角色
     * @param int $offset
     * @param int|null $count null返回全部
     * @return array|boolean
     */
    public function getAppDevelopers($appId, $role = null, $offset = 0, $count = null)
    {
        $where = array(
        	'app_id' => $appId
        );
        if (!is_null($role)) {
            $where['role'] = $role;
        }
        $result = $this->_appDeveloperTable->get($where, $offset, $count);
        if ($result === false) {
        	$this->_logger->log("get failed");
            return false;
        }
        $developerIds = array();
        foreach ($result as $row) {
            $developerIds[] = $row['developer_id'];
        }
        if (!empty($developerIds)) {
            $developerModel = new OaDeveloperModel($this->_logger);
            $developers = $developerModel->getDeveloperMulti($developerIds);
            foreach ($result as &$row) {
                if (isset($developers[$row['developer_id']])) {
                    $developer = $developers[$row['developer_id']];
                    $row['name'] = $developer['name'];
                }
            }
            unset($row);
        }
        return $result;
    }
    
    /**
     * 根据指定条件查询应用开发者列表
     * @param array $where
     * @param int $offset
     * @param int $count
     * @param string $sort
     * @param array $otherWhere
     * @return array|boolean
     */
    public function getAppDeveloperList($where = null, $offset = 0, $count = null, 
        $sort = null, $otherWhere = null)
    {
        return $this->_appDeveloperTable->get($where, $offset, $count, $sort, $otherWhere);
    }
    
    /**
     * 根据指定条件查询应用开发者总数
     * @param array $where
     * @param array $otherWhere
     * @return int|boolean
     */
    public function getAppDeveloperListCount($where = null, $otherWhere = null)
    {
        return $this->_appDeveloperTable->getCount($where, $otherWhere);
    }
    
    /**
     * 修改应用开发人员角色
     * @param $appId
     * @param $developerId
     * @param $role
     */
    public function modifyDeveloper($appId, $developerId, $role)
    {
        return $this->_appDeveloperTable->update(array(
            'app_id' => $appId,
            'developer_id' => $developerId
        ), array(
            'role' => $role
        ));
    }
    
    /**
     * 修改应用开发人员角色
     * @param int $appId
     * @param int $developerId
     * @param int $role
     * @return boolean 如果不是应用人员，设置错误号1
     */
    public function changeDeveloperRole($appId, $developerId, $role)
    {
        if (!is_array($this->getDeveloper($appId, $developerId))) {
            $this->_setError(1, "not a app member");
            return false;
        }
        // 判断新角色是否是管理员
        if ($role == OaAuthorityModel::ROLE_APP_ADMIN) {
            // 删除原有管理员
            $rows = $this->getAppDevelopers($appId, OaAuthorityModel::ROLE_APP_ADMIN);
            if ($rows && !$this->deleteDeveloper($appId, $rows[0]['developer_id'])) {
                $this->_logger->log("delete developer failed");
                return false;
            }
            // 如果该人员在公司中的角色为普通员工，升级到产品经理
            $app = $this->getApp($appId);
            if (!is_array($app)) {
                $this->_logger->log("app not exists");
                return false;
            }
            $companyId = $app['company_id'];
            $developer = $this->_companyModel->getDeveloper($companyId, $developerId);
            if ($developer && $developer['role'] == OaAuthorityModel::ROLE_COMPANY_EMPLOYEE && 
                !$this->_companyModel->changeDeveloperRole($companyId, $developerId, 
                OaAuthorityModel::ROLE_COMPANY_PM)) {
                $this->_logger->log("change developer role in company failed");
                return false;
            }
        }
        return $this->_appDeveloperTable->update(array(
            'app_id' => $appId,
            'developer_id' => $developerId
        ), array(
            'role' => $role
        ));
    }
    
    /**
     * 替换管理员
     * @param int $appId
     * @param string $pt
     * @return boolean 如果pt帐号不存在，设置错误号1
     */
    public function changeAdmin($appId, $pt)
    {
        $result = User::convertId($pt);
        if ($result[0] != 0) {
            $this->_setError(1, "convert id failed: {$result[1]}");
            return false;
        }
        $developerId = $result[2];
        // 如果不在应用下，先加入应用
        if (!is_array($this->getDeveloper($appId, $developerId))) {
            if (!$this->addDeveloper($appId, $pt)) {
                $this->_logger->log("add developer failed, app_id-$appId, pt-$pt");
                return false;
            }
        }
        return $this->changeDeveloperRole($appId, $developerId, OaAuthorityModel::ROLE_APP_ADMIN);
    }
    
    /**
     * 删除应用开发人员
     * @param int $appId
     * @param int $developerId
     * @return boolean
     */
    public function deleteDeveloper ($appId, $developerId)
    {
        return $this->_appDeveloperTable->delete(
        array(
            'app_id' => $appId, 
            'developer_id' => $developerId
        ));
    }
    /**
     * 删除应用开发人员（通过PT帐号）
     * @param int $appId
     * @param string $pt
     * @return boolean 如果pt帐号不存在，设置错误号1
     */
    public function deleteDeveloperByPt ($appId, $pt)
    {
        $result = User::convertId($pt);
        if ($result[0] != 0) {
            $this->_setError(1, "convert id failed: {$result[1]}");
            return false;
        }
        $developerId = $result[2];
        return $this->deleteDeveloper($appId, $developerId);
    }
    
    /**
     * 查询应用配置
     * @param int $appId
     * @param array $columns
     * @param int $env
     * @return array|null|boolean
     */
    public function getAppConfig($appId, $columns = null, $env = null)
    {
    	if (is_null($env)) {
    		$env = $this->_configEnv;
    	}
        $row = $this->_appConfigTable->getRow(array(
            'environment' => $env, 
            'app_id' => $appId
        ), null, $columns);
        if ($row === false) {
            return false;
        }
        if (is_array($row)) {
            $row = $this->_decodeRow($row);
        }
        return $row;
    }
    
    /**
     * 查询应用配置（批量），返回数组以应用ID为Index
     * @param array $appIds
     * @param array $columns
     * @param int $env
     * @return array|boolean
     */
    public function getAppConfigMulti($appIds, $columns = null, $env = null)
    {
        $appConfigs = array();
        foreach ($appIds as $appId) {
            $appConfigs[$appId] = $this->getAppConfig($appId, $columns, $env);
        }
        return $appConfigs;
    }
    
    /**
     * 获取公司应用列表
     * @param int $companyId
     * @param int $offset
     * @param int|null $count null返回全部
     * @return array|boolean
     */
    public function getCompanyApps($companyId, $offset = 0, $count = null)
    {
        return $this->getAppList(array(
        	'company_id' => $companyId
        ), $offset, $count, 'create_time ASC');
    }
    
    /**
     * 邀请盛大用户加入应用
     * @param string $pt 盛大通行证
     * @param int $appId
     * @param int $developerId 邀请者
     * @param string $role
     * @return int|boolean
     */
    public function inviteSndaUser($pt, $appId, $developerId, $role = OaAuthorityModel::ROLE_APP_DEVELOPER)
    {
        // 将之前发给用户的邀请都设为无效
        if (!$this->_inviteSndaUserTable->update(array(
            'type' => 2,
            'app_id' => $appId,
            'pt' => $pt
        ), array(
            'status' => self::APP_INVITE_STATUS_INVALID
        ))) {
            $this->_logger->log("update failed");
            return false;
        }
        
        return $this->_inviteSndaUserTable->insert(array(
            'pt' => $pt,
            'developer_id' => $developerId,
            'type' => 2,
            'app_id' => $appId,
            'role' => $role,
            'status' => 0,
            'create_time' => date('Y-m-d H:i:s')
        ));
    }
    
    /**
     * 查询邀请记录
     * @param int $id
     * @return array|null|boolean
     */
    public function getInvite($id)
    {
    	return $this->_inviteSndaUserTable->getRow(array(
            'id' => $id
    	));
    }
    
    /**
     * 获取邀请码
     * @param int $id 邀请记录ID
     * @return string|boolean
     */
    public function getInviteToken($id)
    {
        $row = $this->_inviteSndaUserTable->getRow(array(
            'id' => $id
        ));
        if (!$row) {
            $this->_logger->log("invite not exist");
            return false;
        }
        return sha1($row['id'] . $row['pt'] . $row['developer_id'] . $row['app_id'] . 
            $row['create_time']);
    }
    
    /**
     * 接受邀请
     * @param int $id
     * @param string $token
     * @return boolean 如果token错误，设置错误号1；如果已经接受过，设置错误号2；
     * 		如果已是应用人员，设置错误号3；如果邀请已失效，设置错误号4
     */
    public function acceptInvite($id, $token)
    {
        if ($token != $this->getInviteToken($id)) {
            $this->_setError(1, "invalid token");
            return false;
        }
        $row = $this->_inviteSndaUserTable->getRow(array(
            'id' => $id
        ));
        if ($row['status'] == self::APP_INVITE_STATUS_ACCEPTED) {
            $this->_setError(2, "already accept");
            return false;
        } else if ($row['status'] == self::APP_INVITE_STATUS_INVALID) {
            $this->_setError(4, "invite is invalid");
            return false;
        }
        if (!$this->setInviteStatus($id, self::APP_INVITE_STATUS_ACCEPTED)) {
            $this->_logger->log("update failed");
            return false;
        }
        $result = User::convertId($row['pt']);
        if ($result[0] != 0) {
            $this->_logger->log("convert id failed: {$result[1]}");
            return false;
        }
        $developerId = $result[2];
        if (is_array($this->getDeveloper($row['app_id'], $developerId))) {
            $this->_setError(3, "already in app");
            return false;
        }
        return $this->addDeveloper($row['app_id'], $row['pt'], $row['role']);
    }
    
    /**
     * 修改邀请状态
     * @param int $id
     * @param int $status
     * @return boolean
     */
    public function setInviteStatus($id, $status)
    {
        return $this->_inviteSndaUserTable->update(array(
            'id' => $id
        ), array(
            'status' => $status
        ));
    }
    /**
     * 添加一个测试帐号，用于测试
     * @param int $appId
     * @param string $pt
     * @param int $creator
     * @param array $row array('environment'=>环境变量,'is_snda'=>是否盛大自身测试账号)
     * @return boolean 如果已添加过，设置错误号1，如果应用测试帐号已经有10个，设置错误号2
     */
    public function addTestAccount ($appId, $pt, $creator, $row = array())
    {
        $this->_logger->info("addTestAccount: app-$appId, pt-$pt, creator-$creator");
        if (! isset($row['environment'])) {
            $row['environment'] = $this->_configEnv;
        }
        if (! isset($row['is_snda'])) {
            $row['is_snda'] = 1;
        }
        $this->_setError(0, "no error");
        if (is_array($this->getTestAccount($appId, $pt, self::APP_CONFIG_ENV_TEST, $row['is_snda']))) {
            $this->_setError(1, "already added");
            return false;
        }
        $numLimit = self::APP_TEST_ACCOUNT_LIMIT;
        if ($row['is_snda'] == 2) {
            $numLimit = self::SNDA_TEST_ACCOUNT_LIMIT;
        }
        if ($this->getAppTestAccountsCount($appId, $row['environment'], $row['is_snda']) >= $numLimit) {
            $this->_setError(2, "number of test accounts up to limit");
            return false;
        }
        $env = $row['environment'];
        $row['environment'] = self::APP_CONFIG_ENV_TEST;        
        $result = $this->_appTestAccountTable->insert(
        array(
            'app_id' => $appId, 
            'pt' => $pt, 
            'creator' => $creator, 
            'create_time' => date('Y-m-d H:i:s')
        ), $row, false);
        if (!$result) {
        	return false;
        }
        $row['environment'] = $env;
        $where = array(
            'environment' => self::APP_CONFIG_ENV_TEST, 
            'app_id' => $appId, 
            'pt' => $pt, 
            'is_snda' => $row['is_snda']
        );
        $message = '';
        if ($row['environment'] == self::APP_CONFIG_ENV_TEST) {
            // 给测试帐号充值                
            $tmpPt = $pt;
            if (is_numeric($tmpPt)) {
                $tmpPt .= self::PT_SUFFIX;
            }
            if (! SDOServiceDeposit::NewDepositCard($message, $tmpPt, self::APP_TEST_ACCOUNT_AMOUNT)) {
                $this->_logger->log($message);
                $this->_appTestAccountTable->delete($where);
                return false;
            }
        }
        // 添加内测属性
        $areas = $this->_appAreaTable->get(
        array(
            'environment' => $row['environment'], 
            'app_id' => $appId
        ), 0, null, null, null, array(
            'area_code'
        ));
        $message = '';
        // 添加内测属性
        if ($areas) {
            $tmpPt = $pt;
            if (is_numeric($tmpPt)) {
                $tmpPt .= self::PT_SUFFIX;
            } elseif (strstr($tmpPt, "@")) {
                $ptResult = User::convertId($tmpPt);
                if ($ptResult[0] != 0) {
                    $this->_setError(3, "convert id failed: {$ptResult[1]}");
                    return false;
                }
            	$tmpPt = $ptResult[1];
            	unset($ptResult);
            }
            foreach ($areas as $row1) {                
                if (isset($row1['area_code']) && ! SDOServiceAuthen::ModifyPermissionUserGame(
                $message, $tmpPt, $appId, $row1['area_code'], 1)) {
                    //$this->_logger->log(
                    //"ModifyPermissionUserGame failure:" . $message);
                    $this->_setError(3, "pt account failed: {$message}");
                    $this->_appTestAccountTable->delete($where);
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * 查询应用测试帐号
     * @param int $appId
     * @param string $pt
     * @param int $env 联调环境，正式环境
     * @param int $isSnda 是否盛大自身测试账号 1不是，2是，null,不选择
     * @return array|null|boolean
     */
    public function getTestAccount ($appId, $pt, $env = null, $isSnda = 1)
    {
        if (! $env) {
            $env = $this->_configEnv;
        }
        $where = array(
            'environment' => self::APP_CONFIG_ENV_TEST, 
            'app_id' => $appId, 
            'pt' => $pt
        );
        if ($isSnda) {
            $where['is_snda'] = $isSnda;
        }
        $row = $this->_appTestAccountTable->getRow($where);
        if ($row && $env == self::APP_CONFIG_ENV_TEST) {
            $message = '';
            if (is_numeric($pt)) {
                $pt .= self::PT_SUFFIX;
            }
            $row['amount'] = SDOServicePayment::accountAuthen($message, $pt);
        }
        return $row;
    }
    /**
     * 查询应用测试帐号列表
     * @param int $appId
     * @param int $offset
     * @param int $count
     * @param int $env 联调环境，正式环境
     * @param int $isSnda 是否盛大自身测试账号 1不是，2是，null,不选择
     * @return array|boolean
     */
    public function getAppTestAccounts ($appId, $offset = 0, $count = null, $env = null, $isSnda = 1)
    {
        if (! $env) {
            $env = $this->_configEnv;
        }
        $where = array(
            'environment' => self::APP_CONFIG_ENV_TEST, 
            'app_id' => $appId
        );
        if ($isSnda) {
            $where['is_snda'] = $isSnda;
        }
        $rows = $this->_appTestAccountTable->get($where, $offset, $count);
        if ($rows && $env == self::APP_CONFIG_ENV_TEST) {
            $message = '';
            foreach ($rows as $k => $row) {
                if (is_numeric($row['pt'])) {
                    $row['pt'] .= self::PT_SUFFIX;
                }
                $rows[$k]['amount'] = SDOServicePayment::accountAuthen($message, 
                $row['pt']);
            }
        }
        return $rows;
    }
    /**
     * 查询应用测试帐号总数
     * @param int $appId
     * @param int $env 联调环境，正式环境
     * @param int $isSnda 是否盛大自身测试账号 1不是，2是，null,不选择
     * @return int|boolean
     */
    public function getAppTestAccountsCount ($appId, $env = null, $isSnda = 1)
    {
        if (! $env) {
            $env = $this->_configEnv;
        }
        $where = array(
            'environment' => self::APP_CONFIG_ENV_TEST, 
            'app_id' => $appId
        );
        if ($isSnda) {
            $where['is_snda'] = $isSnda;
        }
        return $this->_appTestAccountTable->getCount($where);
    }
    /**
     * 删除应用测试帐号
     * @param int $appId
     * @param string $pt
     * @param int $env 联调环境，正式环境
     * @param int $isSnda 是否盛大自身测试账号 1不是，2是
     * @return boolean
     */
    public function deleteTestAccount ($appId, $pt, $env = null, $isSnda = 1)
    {
        if (! $env) {
            $env = $this->_configEnv;
        }
        $where = array(
            'environment' => self::APP_CONFIG_ENV_TEST, 
            'app_id' => $appId, 
            'pt' => $pt, 
            'is_snda' => $isSnda
        );
        $result = $this->_appTestAccountTable->delete($where);
        /*$appRow = $this->getApp($appId, array(
            'pay_type', 
            'company_id'
        ));*/
        unset($where['isSnda']);
        if(!$this->_appTestAccountTable->get($where)) {
            $tmpPt = $pt;
            if (is_numeric($tmpPt)) {
                $tmpPt .= self::PT_SUFFIX;
            } elseif (strstr($tmpPt, "@")) {
                $ptResult = User::convertId($tmpPt);
                if ($ptResult[0] != 0) {
                    $this->_logger->info( "delete test account: convert id failed: {$ptResult[1]}");
                } else {
            	    $tmpPt = $ptResult[1];
                }
            	unset($ptResult);
            }
            if ($result) {
                $areas = $this->_appAreaTable->get(
                array(
                    //                'environment' => $env, 
                    'app_id' => $appId
                ), 0, null, null, null, array(
                    'area_code'
                ));
                $message = '';
                // 删除内测属性
                if ($areas) {
                    foreach ($areas as $row) {
                        if ($row['area_code']) {
                            SDOServiceAuthen::ModifyPermissionUserGame(
                            $message, $tmpPt, $appId, 
                            $row['area_code'], 0);
                        }
                    }
                }
            }
        }
        return $result;
    }
    
    /**
     * 获取当前环境
     * @return int
     */
    public function getCurrentConfigEnv()
    {
        return $this->_configEnv;
    }
    
    /**
     * 设置当前环境
     * @param int $env
     * @return boolean
     */
    public function setCurrentConfigEnv($env)
    {
        if (!in_array($env, array(self::APP_CONFIG_ENV_TEST, self::APP_CONFIG_ENV_PRODUCTION))) {
            $this->_logger->err("invalid env: env-$env");
            return false;
        }
        $this->_configEnv = $env;
        return true;
    }
    
    private static function _splitRow($row)
    {
        $oaAppRow = array();
        $oaAppConfigRow = array();
        foreach ($row as $key => $value) {
            if (in_array($key, self::$_configColumns)) {
                $oaAppConfigRow[$key] = $value;
            } else {
                $oaAppRow[$key] = $value;
            }
        }
        return array($oaAppRow, $oaAppConfigRow);
    }
    
    private static function _splitColumns($columns)
    {
        $oaAppColumns = array();
        $oaAppConfigColumns = array();
        foreach ($columns as $value) {
            if (in_array($value, self::$_configColumns)) {
                $oaAppConfigColumns[] = $value;
            } else {
                $oaAppColumns[] = $value;
            }
        }
        return array($oaAppColumns, $oaAppConfigColumns);
    }
    
    private static function _encodeRow($row)
    {
        foreach ($row as $key => &$value) {
            if (in_array($key, self::$_jsonColumns)) {
                $value = json_encode($value);
            }
        }
        unset($value);
        return $row;
    }
    
    private static function _decodeRow($row)
    {
        foreach ($row as $key => &$value) {
            if (in_array($key, self::$_jsonColumns)) {
                $value = json_decode($value, true);
            }
        }
        unset($value);
        return $row;
    }
    
    private function _getSdkVersion($ips, $env = 1)
    {
    	if (empty($ips)) {
    		return array();
    	}
        $message = '';
        $result = OASApi::getSdkVersion($message, $ips, $env);
        if (is_array($result)) {
            $a = array();
            foreach ($result as $v) {
            	$a["{$v[0]}_{$v[1]}"] = $v[2];
            }
            return $a;
        } else {
            return array();
        }
    }
    
    /**
     * 生成App ID，按100分段
     * 100000000~200000000为内部保留
     * @param boolean $reserved 是否使用保留段
     * @return int|boolean
     */
    private function _generateAppId($reserved)
    {
        if (__OA_ENV == 'ONLINE') {
            if ($reserved) {
                $sql = "SELECT MAX(id) FROM app WHERE id>=100000000 AND id<200000000";
                $id = $this->_appTable->queryOne($sql);
                if ($id === false) {
                    $this->_logger->log("queryOne failed");
                    return false;
                }
                if (is_null($id)) {
                    $id = 100000000;
                } else {
                    $id += 100;
                }
            } else {
                $sql = "SELECT MAX(id) FROM app WHERE id>=200000000";
                $id = $this->_appTable->queryOne($sql);
                if ($id === false) {
                    $this->_logger->log("queryOne failed");
                    return false;
                }
                if (is_null($id)) {
                    $id = 200000000;
                } else {
                    $id += 100;
                }
            }
        } else {
            $sql = "SELECT MAX(id) FROM app";
            $id = $this->_appTable->queryOne($sql);
            if ($id === false) {
                $this->_logger->log("queryOne failed");
                return false;
            }
            if (is_null($id)) {
                $id = 100000000;
            } else {
                $id += 100;
            }
        }
        $id = intval($id / 100) * 100;
        return $id;
    }
}
?>